package svgedit.svg;

import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.GeneralPath;
import java.util.ArrayList;

import svgedit.gui.Marker;
import svgedit.gui.Marker.MarkerType;
import svgedit.gui.SelectionRectStroke;
import svgedit.gui.ControlPointsStroke;

/**
 * An SVG visual element.
 * 
 * The interfaces defined here roughly correspond to the SVG DOM interfaces
 * defined in {@literal 4.5.1 Interface SVGElement}, but are modified to suit
 * the restricted implementation required here.
 * 
 * All SVG elements have a link to their parent and the document they belong to.
 * These links are maintained automatically as elements are inserted into and
 * removed from groups.
 */
public abstract class SVGElement implements SVGElementInterface, SVGDrawable
{
	private SVGGroup parent;
	private ArrayList<Marker> markers = null;

	/**
	 * Creates an element within a document.
	 * 
	 * @param parent
	 *            the parent that this element belongs to
	 */
	public SVGElement(SVGGroup parent)
	{
		this.parent = parent;
		this.markers = new ArrayList<Marker>();
	}

	/**
	 * Gets the document this element belongs to.
	 * 
	 * @return an SVG document
	 */
	public SVGDocument getDocument()
	{
		// recursively get the document by calling the parent SVGRootGroup
		return this.getParent().getDocument();
	}

	public SVGRootGroup getRootGroup()
	{
		return this.parent.getRootGroup();
	}

	/**
	 * Returns a group. The group returned is a child of the root group. It is
	 * also an ancestor of the descendant group you call this method on. It can
	 * in fact also be this group.
	 * 
	 * @return Returns god father group
	 */
	@Override
	public SVGGroup getGodFatherGroup()
	{
		SVGGroup group = this.getParent();

		while (!(group.getParent() instanceof SVGRootGroup))
			group = group.getParent();

		System.out.println("SVGGroup.getGodFatherGroup Godfather is " + group);
		return group;
	}

	/**
	 * Gets the group element that contains this element.
	 * 
	 * @return the parent group, or null if the group is the document root group
	 */
	public SVGGroup getParent()
	{
		return parent;
	}

	/**
	 * Sets the parent of this element. Called only from Group as a result of
	 * child list modifications.
	 * 
	 * @param parent
	 *            the parent that contains this group
	 */
	final void setParent(SVGGroup parent)
	{
		this.parent = parent;
	}

	@Override
	public void destroy()
	{
		System.out.println("SVGElement.destroy " + this);
		this.getParent().removeChild(this);
	}

	@Override
	public abstract Shape createShape();

	@Override
	public Rectangle getBounds()
	{
		return this.createShape().getBounds();
	}

	@Override
	public ArrayList<Marker> getMarkers()
	{
		return this.markers;
	}

	/**
	 * Instantiates a Marker using the params supplies and adds it to the
	 * elements array list of markers
	 * 
	 * @see svgedit.gui.Marker#Marker(SVGElement, GeneralPath, float, float,
	 *      MarkerType)
	 * @param strokedShape
	 * @param x
	 * @param y
	 * @param mType
	 * @return
	 */
	protected ArrayList<Marker> addMarker(Shape strokedShape, double x,
			double y, MarkerType mType)
	{

		Marker marker = new Marker(this, (GeneralPath) strokedShape, (int) x,
				(int) y, mType);

		this.markers.add(marker);

		return this.markers;
	}

	public ControlPointsStroke getContolPointsStroke()
	{
		ControlPointsStroke stroke = new ControlPointsStroke(0.5f, this);
		stroke.createStrokedShape(this.createShape());
		return stroke;
	}

	@Override
	public Shape getSelectionStroke()
	{
		// Start off by stroking the shape with a thin line. Store the
		// resulting shape in a GeneralPath object so we can add to it.

		// init or clear markers - array list now init in constructor
		if (this.markers == null)
			this.markers = new ArrayList<Marker>();
		else
			this.markers.clear();

		// Create selection rect around shape
		GeneralPath path = new GeneralPath(
				new SelectionRectStroke().createStrokedShape(this.createShape()
						.getBounds2D()));
		return path;

	}

	public abstract void write(int indent);

	/** Calls the appropriate visit method on the given visitor. */
	public abstract void acceptVisitor(SVGVisitor visitor);

}
